package com.atguigu.krp.handler;

import com.atguigu.krp.common.protocol.ProxyMessage;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.ChannelMatcher;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;
import jdk.nashorn.internal.runtime.PrototypeObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @author KEIO  on 2022/1/13.
 * @version 1.0
 */
@Slf4j
@Component
public class ServerHandler extends ChannelInboundHandlerAdapter {

    //TODO  check register services

    private Map<String, Integer> clients = new HashMap<String, Integer>();

    private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private EventLoopGroup bossGroup = new NioEventLoopGroup();
    private EventLoopGroup workerGroup = new NioEventLoopGroup();

    private ChannelHandlerContext ctx;


    private Boolean isRegister = false;


    private String clientKey;



    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        log.info("有内网客户端建立连接, 地址为{}", ctx.channel().remoteAddress());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        channels.remove(ctx.channel());
        ctx.channel().close();
        clients.remove(clientKey);

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

        log.info("内网客户端{} 连接关闭", ctx.channel().remoteAddress());


    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ProxyMessage proxyMessage = (ProxyMessage) msg;
        int type = proxyMessage.getType();
        if(type == ProxyMessage.TYPE_REGISTER) {
            processRegister(proxyMessage);

        } else if(isRegister) {
            switch (type) {
                case ProxyMessage.TYPE_DISCONNECTED: {

                }
                case ProxyMessage.TYPE_DATA: {
                    processData(proxyMessage);
                    break;
                }
                case ProxyMessage.TYPE_KEEPALIVE: {
                    //心跳连接 不做任何处理
                    break;
                }
            }

        } else {
            ctx.channel().close();
            log.info("有非法的内网客户端访问, 来自{}", ctx.channel().remoteAddress());
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        log.info("连接异常, 连接关闭");
        cause.printStackTrace();

    }

    private void processData(ProxyMessage proxyMessage) {
        if(proxyMessage.getData() == null || proxyMessage.getData().length < 1) {
            // 数据为空
            return;
        }
        log.info("执行数据 转发到外网的逻辑");
        channels.writeAndFlush(proxyMessage.getData(), new ChannelMatcher() {
            @Override
            public boolean matches(Channel channel) {
                /*
                通过数据包中的内置metaData进行匹配
                 */
                if(channel.id().asLongText().equals(proxyMessage.getMetaData().get("channelId").toString())) {
                    // 匹配成功 就是数据包对应的channelId
                    //TODO 日志记录
                    return true;
                } else {
                    return false;
                }
            }
        });

        log.info("收到内网客户端返回的数据 共 {}", proxyMessage.getData().length);


    }


    private Boolean isExist(String clientKey) {
        int flag = clients.get(clientKey);
        if(flag > 0) {
            return true;
        } else {
            return false;
        }
    }


    /*
    防止 多线程下 客户端密钥重用
     */
    private synchronized Boolean isLegal(String clientKey) {
        //TODO check client key in database
        if(isExist(clientKey)) {


        }
        this.clientKey = clientKey;
        clients.put(clientKey, 1);
        return true;




    }

    /*
     执行内网客户端 注册逻辑
     */
    private void processRegister(ProxyMessage proxyMessage) {
        ServerHandler serverHandler = this;

        Map<String, Object> metaData = proxyMessage.getMetaData();
        String clientKey = (String) metaData.get("clientKey");
        if(!ObjectUtils.isEmpty(clientKey)) {
            if(isLegal(clientKey)) {
                // server host
                String host = "0.0.0.0";
                //指定服务器需要开启的对外端口
                ArrayList<Integer> ports = (ArrayList<Integer>) metaData.get("ports");

                try {
                    for(int port: ports) {
                        ChannelInitializer channelInitializer = new ChannelInitializer() {
                            @Override
                            protected void initChannel(Channel channel) throws Exception {
                                RemoteHandler remoteHandler = new RemoteHandler();
                                //TODO remoteHandler set value
                                channel.pipeline().addLast(
                                        new ByteArrayDecoder(),
                                        new ByteArrayEncoder(),
                                        remoteHandler
                                );
                                channels.add(channel);

                            }
                        };
                        metaData.put("isSuccess", true);
                        isRegister = true;
                        //TODO 绑定启动
                    }
                } catch (Exception e) {
                    log.info("启动器出现异常");
                    metaData.put("isSuccess", false);
                    metaData.put("reason", e.getMessage());

                    e.printStackTrace();
                }

            } else {
                metaData.put("isSuccess", false);
                log.info("客户端密钥不合法");
            }

            ProxyMessage resp = new ProxyMessage();
            resp.setType(ProxyMessage.TYPE_AUTH);
            resp.setMetaData(metaData);
            ctx.writeAndFlush(resp);
        } else {
            log.info("密钥为空");
        }

    }
}
